

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const BasicStrategy = require('passport-http').BasicStrategy;
const ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
const BearerStrategy = require('passport-http-bearer').Strategy;
const db = require('../db');

/**
 * LocalStrategy 本地策略
 * 此策略用于根据用户名和密码对用户进行身份验证。
 * 任何时候提出申请授权，我们必须确保用户登录后才要求他们批准请求。
 */
passport.use(new LocalStrategy(
  (username, password, done) => {
    db.users.findByUsername(username, (error, user) => {
      if (error) return done(error);
      if (!user) return done(null, false);
      if (user.password !== password) return done(null, false);
      return done(null, user);
    });
  }
));

passport.serializeUser((user, done) => done(null, user.id));

passport.deserializeUser((id, done) => {
  db.users.findById(id, (error, user) => done(error, user));
});

/**
 * BasicStrategy & ClientPasswordStrategy 基本策略和客户端密码策略
 *
 * 这些策略用于验证已注册的OAuth客户端。
 * 它们用于保护“令牌”端点，消费者使用该端点来获取访问令牌。
 * OAuth 2.0规范建议客户端使用HTTP基本方案进行身份验证。
 * 使用客户端密码策略允许客户端在请求体中发送相同的凭据（与“授权”头相反）。
 * 虽然规范不推荐这种方法，但在实践中它是很常见的。
 */
function verifyClient(clientId, clientSecret, done) {
  db.clients.findByClientId(clientId, (error, client) => {
    if (error) return done(error);
    if (!client) return done(null, false);
    if (client.clientSecret !== clientSecret) return done(null, false);
    return done(null, client);
  });
}

passport.use(new BasicStrategy(verifyClient));

passport.use(new ClientPasswordStrategy(verifyClient));

/**
 * BearerStrategy Bearer策略
 *
 * 此策略用于基于访问令牌（又称承载令牌）对用户或客户端进行身份验证。
 * 如果是用户，则必须事先授权客户端应用程序，该应用程序将获得访问令牌以代表授权用户发出请求。
 */
passport.use(new BearerStrategy(
  (accessToken, done) => {
    db.accessTokens.find(accessToken, (error, token) => {
      if (error) return done(error);
      if (!token) return done(null, false);
      if (token.userId) {
        db.users.findById(token.userId, (error, user) => {
          if (error) return done(error);
          if (!user) return done(null, false);
          //为了保持这个例子的简单，没有实现限制作用域，
          //这只是为了说明的目的。
          done(null, user, { scope: '*' });
        });
      } else {
        //请求来自客户端，因为userId为null
        //因此，返回的是客户端而不是用户。
        db.clients.findByClientId(token.clientId, (error, client) => {
          if (error) return done(error);
          if (!client) return done(null, false);
          //为了保持这个例子的简单，没有实现限制作用域，
          //这只是为了说明的目的。
          done(null, client, { scope: '*' });
        });
      }
    });
  }
));
